local MAX_FOLLOW_DIST = 10
local SEE_TARGET_DIST = 15
local KEEP_FACE_DIST = 6
local RUN_AWAY_DIST = 7
local SEE_WORK_DIST = 25
local KEEP_WORKING_DIST = 35
local COMBAT_SAFE_TO_WATCH_FROM_MAX_DIST = 12
local COMBAT_SAFE_TO_WATCH_FROM_MAX_DIST_SQ = COMBAT_SAFE_TO_WATCH_FROM_MAX_DIST * COMBAT_SAFE_TO_WATCH_FROM_MAX_DIST
local COMBAT_TIMEOUT = 6
local COMBAT_TOO_CLOSE_DIST = 5
local COMBAT_TOO_CLOSE_DIST_SQ = COMBAT_TOO_CLOSE_DIST * COMBAT_TOO_CLOSE_DIST
local NO_LOOTING_TAGS = {"INLIMBO", "catchable", "fire", "irreplaceable", "heavy", "outofreach"}
local NO_PICKUP_TAGS = deepcopy(NO_LOOTING_TAGS)
table.insert(NO_PICKUP_TAGS, "_container")

local function _avoidtargetfn(self, target)
	if target == nil or not target:IsValid() then
		return false
	end

	local owner = self.inst.components.follower.leader
	local owner_combat = owner ~= nil and owner.components.combat or nil
	local target_combat = target.components.combat
	if owner_combat == nil or target_combat == nil then
		return false
	elseif
		target_combat:TargetIs(owner) or
			(target.components.grouptargeter ~= nil and target.components.grouptargeter:IsTargeting(owner))
	 then
		return true
	end

	local distsq = owner:GetDistanceSqToInst(target)
	if distsq >= COMBAT_SAFE_TO_WATCH_FROM_MAX_DIST_SQ then
		-- Too far
		return false
	elseif distsq < COMBAT_TOO_CLOSE_DIST_SQ and target_combat:HasTarget() then
		-- Too close to any combat
		return true
	end

	-- Is owner in combat with target?
	-- Are owner and target both in any combat?
	local t = GetTime()
	return ((owner_combat:IsRecentTarget(target) or target_combat:HasTarget()) and
		math.max(owner_combat.laststartattacktime, owner_combat.lastdoattacktime or 0) + COMBAT_TIMEOUT > t) or
		(owner_combat.lastattacker == target and owner_combat:GetLastAttackedTime() + COMBAT_TIMEOUT > t)
end

local function HasStateTags(inst, tags)
	for k, v in pairs(tags) do
		if inst.sg:HasStateTag(v) then
			return true
		end
	end
end

local function StartWorkingCondition(inst, actiontags)
	return inst.components.follower.leader and HasStateTags(inst.components.follower.leader, actiontags) and
		not HasStateTags(inst, actiontags)
end

local function KeepWorkingAction(inst, actiontags)
	return inst.components.follower.leader and
		inst.components.follower.leader:GetDistanceSqToInst(inst) <= KEEP_WORKING_DIST * KEEP_WORKING_DIST and
		HasStateTags(inst.components.follower.leader, actiontags)
end

local function FindObjectToWorkAction(inst, action)
	if inst.sg:HasStateTag("working") then
		return
	end
	local target =
		FindEntity(
		inst.components.follower.leader,
		SEE_WORK_DIST,
		function(item)
			return item.components.workable and item.components.workable.action == action
		end
	)
	if target then
		--print(GetTime(), target)
		return BufferedAction(inst, target, action)
	end
end

local function GetFaceTargetFn(inst)
	if not (inst.components.domesticatable ~= nil and inst.components.domesticatable:IsDomesticated()) then
		local target = inst.components.follower and inst.components.follower.leader
		return target ~= nil and not target:HasTag("notarget") and target or nil
	end
end

local function KeepFaceTargetFn(inst, target)
	return inst ~= nil and target ~= nil and inst:IsValid() and target:IsValid() and
		not (target:HasTag("notarget") or target:HasTag("playerghost")) and
		inst:IsNear(target, KEEP_FACE_DIST)
end

local function CombatAvoidanceFindEntityCheck(self)
	return function(ent)
		if _avoidtargetfn(self, ent) then
			self.inst:PushEvent("critter_avoidcombat", {avoid = true})
			self.runawayfrom = ent
			return true
		end
		return false
	end
end

local function pick_item(inst)
	if inst.sg:HasStateTag("busy") then
		return
	end
	musha_print("Yamche find item to pick")
	local not_targets = {}
	local lootchests = inst.canlootchests ~= false --nil defaults to true
	local x, y, z = inst.Transform:GetWorldPosition()
	local players = TheSim:FindEntities(x, y, z, SEE_WORK_DIST * 2, {"player"})
	for _, player in pairs(players) do
		if player then
			local player_action = player:GetBufferedAction()
			if player_action and player_action.action == ACTIONS.PICKUP then
				musha_print("Yamche add item not to pick")
				table.insert(not_targets, player_action.target)
			end
		end
	end
	local ents =
		lootchests and TheSim:FindEntities(x, y, z, SEE_WORK_DIST, nil, NO_LOOTING_TAGS, {"_inventoryitem", "_container"}) or
		TheSim:FindEntities(x, y, z, SEE_WORK_DIST, {"_inventoryitem"}, NO_PICKUP_TAGS)

	for i, v in ipairs(ents) do
		for _, not_target in pairs(not_targets) do
			if not not_target == v then
				if v.components.inventoryitem ~= nil and v.components.inventoryitem.canbepickedup and v.components.container == nil then
					musha_print("Yamche pick item", v)
					return BufferedAction(inst, v, ACTIONS.PICKUP)
				end
			end
		end
	end
end

local musha_yamchebrain =
	Class(
	Brain,
	function(self, inst)
		Brain._ctor(self, inst)
	end
)

function musha_yamchebrain:OnStart()
	local root =
		PriorityNode(
		{
			WhileNode(
				function()
					return self.inst.components.health.takingfiredamage
				end,
				"OnFire",
				Panic(self.inst)
			),
			SequenceNode {
				ConditionNode(
					function()
						return self.inst.action == "avoid"
					end,
					"Avoid"
				),
				RunAway(
					self.inst,
					{tags = {"_combat", "_health"}, notags = {"wall", "INLIMBO"}, fn = CombatAvoidanceFindEntityCheck(self)},
					RUN_AWAY_DIST,
					SEE_TARGET_DIST
				)
			},
			WhileNode(
				function()
					return self.inst.components.health:GetPercent() > 0.3 and self.inst.components.combat.target == nil or
						not self.inst.components.combat:InCooldown() and self.inst.action == "fight"
				end,
				"AttackMomentarily",
				ChaseAndAttack(self.inst, 8, 12)
			),
			WhileNode(
				function()
					return self.inst.components.health:GetPercent() > 0.3 and self.inst.components.combat.target and
						self.inst.components.combat:InCooldown()
				end,
				"Dodge",
				RunAway(
					self.inst,
					function()
						return self.inst.components.combat.target
					end,
					3,
					10
				)
			),
			--ChaseAndAttack(self.inst, 10, 14),
			WhileNode(
				function()
					return self.inst.action == "pick"
				end,
				"Pick",
				DoAction(self.inst, pick_item)
			),
			SequenceNode {
				ConditionNode(
					function()
						return self.inst.components.follower and self.inst.components.follower.leader and true or false
					end,
					"HasLeader"
				),
				WhileNode(
					function()
						return StartWorkingCondition(self.inst, {"chopping", "prechop"}) and
							KeepWorkingAction(self.inst, {"chopping", "prechop"})
					end,
					"keep chopping",
					DoAction(
						self.inst,
						function()
							return FindObjectToWorkAction(self.inst, ACTIONS.CHOP)
						end
					)
				),
				WhileNode(
					function()
						return StartWorkingCondition(self.inst, {"mining", "premine"}) and
							KeepWorkingAction(self.inst, {"mining", "premine"})
					end,
					"keep mining",
					DoAction(
						self.inst,
						function()
							return FindObjectToWorkAction(self.inst, ACTIONS.MINE)
						end
					)
				)
			},
			Follow(self.inst, GetFaceTargetFn, 0, 3, 6, true),
			FaceEntity(self.inst, GetFaceTargetFn, KeepFaceTargetFn),
			Wander(
				self.inst,
				function()
					if self.inst.components.follower.leader then
						return Vector3(self.inst.components.follower.leader.Transform:GetWorldPosition())
					end
				end,
				5,
				{minwalktime = .5, randwalktime = .5, minwaittime = 6, randwaittime = 3}
			)
			--[[ Wander(
				self.inst,
				function()
					if self.inst.components.follower.leader then
						return Vector3(self.inst.components.follower.leader.Transform:GetWorldPosition())
					end
				end,
				15,
				{minwalktime = .5, randwalktime = .5, minwaittime = 6, randwaittime = 3}
			) ]]
		},
		.25
	)
	self.bt = BT(self.inst, root)
end

return musha_yamchebrain
